using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace HalconDotNet
{
	internal abstract class HTupleImplementation
	{
		public const int INTEGER = 1;

		public const int DOUBLE = 2;

		public const int STRING = 4;

		public const int HANDLE = 16;

		public const int ANY_ELEM = 23;

		public const int MIXED = 8;

		public const int ANY_TUPLE = 31;

		public const int LONG = 129;

		public const int FLOAT = 32898;

		public const int INTPTR = 32900;

		public const int BAN_IN_MIXED = 32768;

		protected Array data;

		protected int iLength;

		internal GCHandle pinHandle;

		internal int pinCount;

		protected Type typeI = typeof(int);

		protected Type typeL = typeof(long);

		protected Type typeD = typeof(double);

		protected Type typeS = typeof(string);

		protected Type typeH = typeof(HHandle);

		protected Type typeO = typeof(object);

		protected Type typeF = typeof(float);

		protected Type typeIP = typeof(IntPtr);

		protected int Capacity => data.Length;

		public int Length => iLength;

		public virtual HTupleType Type
		{
			get
			{
				throw new HTupleAccessException(this);
			}
		}

		public virtual int[] IArr
		{
			get
			{
				throw new HTupleAccessException(this);
			}
			set
			{
				throw new HTupleAccessException(this);
			}
		}

		public virtual long[] LArr
		{
			get
			{
				throw new HTupleAccessException(this);
			}
			set
			{
				throw new HTupleAccessException(this);
			}
		}

		public virtual double[] DArr
		{
			get
			{
				throw new HTupleAccessException(this);
			}
			set
			{
				throw new HTupleAccessException(this);
			}
		}

		public virtual string[] SArr
		{
			get
			{
				throw new HTupleAccessException(this);
			}
			set
			{
				throw new HTupleAccessException(this);
			}
		}

		public virtual HHandle[] HArr
		{
			get
			{
				throw new HTupleAccessException(this);
			}
			set
			{
				throw new HTupleAccessException(this);
			}
		}

		public virtual object[] OArr
		{
			get
			{
				throw new HTupleAccessException(this);
			}
			set
			{
				throw new HTupleAccessException(this);
			}
		}

		public static int GetObjectType(object o)
		{
			if (o is int)
			{
				return 1;
			}
			if (o is long)
			{
				return 129;
			}
			if (o is double)
			{
				return 2;
			}
			if (o is float)
			{
				return 32898;
			}
			if (o is string)
			{
				return 4;
			}
			if (o is HHandle)
			{
				return 16;
			}
			if (o is IntPtr)
			{
				return 32900;
			}
			return 31;
		}

		public static int GetObjectsType(object[] o)
		{
			if (o == null)
			{
				return 31;
			}
			int num = 31;
			int num2 = 31;
			for (int i = 0; i < o.Length; i++)
			{
				if (o[i] is int)
				{
					num = 1;
				}
				if (o[i] is long)
				{
					num = 129;
				}
				if (o[i] is double)
				{
					num = 2;
				}
				if (o[i] is float)
				{
					num = 32898;
				}
				if (o[i] is string)
				{
					num = 4;
				}
				if (o[i] is HHandle)
				{
					num = 16;
				}
				if (o[i] is IntPtr)
				{
					num = 32900;
				}
				if (i == 0)
				{
					num2 = num;
				}
				else if (num != num2)
				{
					return 8;
				}
			}
			return num2;
		}

		public static HTupleImplementation CreateInstanceForType(HTupleType type, int size = 0)
		{
			switch (type)
			{
			case HTupleType.EMPTY:
				return HTupleVoid.EMPTY;
			case HTupleType.INTEGER:
				return new HTupleInt32(new int[size], copy: false);
			case HTupleType.LONG:
				return new HTupleInt64(new long[size], copy: false);
			case HTupleType.DOUBLE:
				return new HTupleDouble(new double[size], copy: false);
			case HTupleType.STRING:
				return new HTupleString(new string[size], copy: false);
			case HTupleType.HANDLE:
				return new HTupleHandle(new HHandle[size], copy: false);
			case HTupleType.MIXED:
				return new HTupleMixed(new object[size], copy: false);
			default:
				throw new HTupleAccessException("Unknown HTupleType requested in TupleImplementation.CreateInstanceForType");
			}
		}

		internal virtual void PinTuple()
		{
		}

		internal void UnpinTuple()
		{
			Monitor.Enter(this);
			if (pinCount > 0)
			{
				pinCount--;
				if (pinCount == 0)
				{
					pinHandle.Free();
				}
			}
			Monitor.Exit(this);
		}

		protected abstract Array CreateArray(int size);

		protected void SetArray(Array source, bool copy)
		{
			if (source == null)
			{
				source = CreateArray(0);
			}
			if (copy)
			{
				data = CreateArray(source.Length);
				Array.Copy(source, data, source.Length);
			}
			else
			{
				data = source;
			}
			iLength = data.Length;
			NotifyArrayUpdate();
		}

		protected virtual void NotifyArrayUpdate()
		{
		}

		public virtual void Dispose()
		{
		}

		public virtual void AssertSize(int index)
		{
			if (index >= iLength)
			{
				if (index >= data.Length)
				{
					Array sourceArray = data;
					data = CreateArray(Math.Max(10, 2 * index));
					Array.Copy(sourceArray, data, iLength);
					NotifyArrayUpdate();
				}
				iLength = index + 1;
			}
		}

		public virtual void AssertSize(int[] indices)
		{
			int num;
			if (indices.Length == 0)
			{
				num = 0;
			}
			else
			{
				num = indices[0];
				foreach (int num2 in indices)
				{
					if (num2 > num)
					{
						num = num2;
					}
				}
			}
			AssertSize(num);
		}

		public virtual HTupleElements GetElement(int index, HTuple parent)
		{
			throw new HTupleAccessException(this);
		}

		public virtual HTupleElements GetElements(int[] indices, HTuple parent)
		{
			if (indices == null || indices.Length == 0)
			{
				return new HTupleElements();
			}
			throw new HTupleAccessException(this);
		}

		public virtual void SetElements(int[] indices, HTupleElements elements)
		{
			if (indices == null || indices.Length == 0)
			{
				return;
			}
			throw new HTupleAccessException(this);
		}

		protected Array ToArray(Type t)
		{
			Array array = Array.CreateInstance(t, iLength);
			Array.Copy(data, array, iLength);
			return array;
		}

		public virtual int[] ToIArr()
		{
			throw new HTupleAccessException(this, "Cannot convert to int array");
		}

		public virtual long[] ToLArr()
		{
			throw new HTupleAccessException(this, "Cannot convert to long array");
		}

		public virtual double[] ToDArr()
		{
			throw new HTupleAccessException(this, "Cannot convert to double array");
		}

		public virtual string[] ToSArr()
		{
			string[] array = new string[iLength];
			for (int i = 0; i < iLength; i++)
			{
				array[i] = data.GetValue(i).ToString();
			}
			return array;
		}

		public virtual HHandle[] ToHArr()
		{
			throw new HTupleAccessException(this, "Cannot convert to handle array");
		}

		public virtual object[] ToOArr()
		{
			return (object[])ToArray(typeO);
		}

		public virtual float[] ToFArr()
		{
			throw new HTupleAccessException(this, "Cannot convert to float array");
		}

		public virtual IntPtr[] ToIPArr()
		{
			throw new HTupleAccessException(this, "Values in tuple do not represent pointers on this platform");
		}

		public virtual int CopyToIArr(int[] dst, int offset)
		{
			Array array = ToArray(typeI);
			Array.Copy(array, 0, dst, offset, array.Length);
			return array.Length;
		}

		public virtual int CopyToLArr(long[] dst, int offset)
		{
			Array array = ToArray(typeL);
			Array.Copy(array, 0, dst, offset, array.Length);
			return array.Length;
		}

		public virtual int CopyToDArr(double[] dst, int offset)
		{
			Array array = ToArray(typeD);
			Array.Copy(array, 0, dst, offset, array.Length);
			return array.Length;
		}

		public virtual int CopyToSArr(string[] dst, int offset)
		{
			Array array = ToArray(typeS);
			Array.Copy(array, 0, dst, offset, array.Length);
			return array.Length;
		}

		public virtual int CopyToHArr(HHandle[] dst, int offset)
		{
			Array array = ToArray(typeH);
			Array.Copy(array, 0, dst, offset, array.Length);
			return array.Length;
		}

		public virtual int CopyToOArr(object[] dst, int offset)
		{
			Array.Copy(data, 0, dst, offset, iLength);
			return data.Length;
		}

		public abstract int CopyFrom(HTupleImplementation impl, int offset);

		public virtual void Store(IntPtr proc, int parIndex)
		{
			HTupleType type;
			switch (Type)
			{
			case HTupleType.INTEGER:
			case HTupleType.LONG:
				type = HTupleType.INTEGER;
				break;
			case HTupleType.DOUBLE:
			case HTupleType.STRING:
			case HTupleType.MIXED:
			case HTupleType.HANDLE:
				type = Type;
				break;
			default:
				type = HTupleType.MIXED;
				break;
			}
			HalconAPI.HCkP(proc, HalconAPI.CreateInputTuple(proc, parIndex, iLength, type, out IntPtr tuple));
			StoreData(proc, tuple);
		}

		protected abstract void StoreData(IntPtr proc, IntPtr tuple);

		public static int Load(IntPtr proc, int parIndex, HTupleType type, out HTupleImplementation data)
		{
			HalconAPI.GetOutputTuple(proc, parIndex, false, out IntPtr tuple);
			return LoadData(tuple, type, out data, force_utf8: false);
		}

		public static int LoadData(IntPtr tuple, HTupleType type, out HTupleImplementation data, bool force_utf8)
		{
			int result = 2;
			if (tuple == IntPtr.Zero)
			{
				data = HTupleVoid.EMPTY;
				return result;
			}
			HalconAPI.GetTupleTypeScanElem(tuple, out int type2);
			switch (type2)
			{
			case 31:
				data = HTupleVoid.EMPTY;
				type = HTupleType.EMPTY;
				break;
			case 1:
				if (HalconAPI.isPlatform64)
				{
					result = HTupleInt64.Load(tuple, out HTupleInt64 hTupleInt);
					data = hTupleInt;
				}
				else
				{
					result = HTupleInt32.Load(tuple, out HTupleInt32 hTupleInt2);
					data = hTupleInt2;
				}
				type = HTupleType.INTEGER;
				break;
			case 2:
			{
				result = HTupleDouble.Load(tuple, out HTupleDouble hTupleDouble);
				data = hTupleDouble;
				type = HTupleType.DOUBLE;
				break;
			}
			case 4:
			{
				result = HTupleString.Load(tuple, out HTupleString hTupleString, force_utf8);
				data = hTupleString;
				type = HTupleType.STRING;
				break;
			}
			case 16:
			{
				result = HTupleHandle.Load(tuple, out HTupleHandle hTupleHandle);
				data = hTupleHandle;
				type = HTupleType.HANDLE;
				break;
			}
			case 23:
			{
				result = HTupleMixed.Load(tuple, out HTupleMixed hTupleMixed, force_utf8);
				data = hTupleMixed;
				type = HTupleType.MIXED;
				break;
			}
			default:
				data = HTupleVoid.EMPTY;
				result = 7002;
				break;
			}
			return result;
		}
	}
}
